﻿using System;
using System.Drawing;
using System.Windows.Forms;
using aboveXWords.Properties;

namespace aboveXWords.GUI
{
  [Serializable]
  public class ResizableSquare : Square
  {
    public ResizableSquare(float x, float y, float width, float height)
    {
      HandleCollection = new HandleCollection
                           {
                             new Handle(this),
                             new Handle(this),
                             new Handle(this),
                             new Handle(this),
                             new Handle(this),
                             new Handle(this),
                             new Handle(this),
                             new Handle(this)
                           };
      Rectangle = new RectangleF(x, y, width, height);
    }

    protected ResizableSquare(Square square)
      : this(square.Rectangle.X, square.Rectangle.Y, square.Rectangle.Width, square.Rectangle.Height)
    {
    }

    public override sealed RectangleF Rectangle
    {
      set
      {
        m_rectangle = value;
        PositionHandles();
        CreateGraphicsPath();
      }
    }

    public HandleCollection HandleCollection { get; set; }

    public override RectangleF Bound
    {
      get
      {
        float deltaX = HandleCollection[0].Width/2.0f + 1;
        float deltaY = HandleCollection[0].Height/2.0f + 1;
        return new RectangleF(Rectangle.X - deltaX,
                              Rectangle.Y - deltaY,
                              Rectangle.Width + 2*deltaX,
                              Rectangle.Height + 2*deltaY);
      }
    }

    protected internal override void Paint(Graphics graphics)
    {
      base.Paint(graphics);
      if (Selected)
        HandleCollection.Paint(graphics);
    }

    protected internal override void CreateGraphicsPath()
    {
      base.CreateGraphicsPath();
      HandleCollection.CreateGraphics();
    }

    //protected internal override Cursor Move(Canvas canvas, MouseEventArgs mouseEventArgs, MouseEventArgs mouseDownPosition)
    //{
    //  // Check for handle resize
    //  Cursor cursor = HandleHit(mouseEventArgs);
    //  if (cursor == Cursors.Default) cursor = base.Move(canvas, mouseEventArgs, mouseDownPosition);
    //  return cursor;
    //}

    protected void PositionHandles()
    {
      //upper left handle
      HandleCollection[0].PointF = new PointF(Rectangle.Left, Rectangle.Top);
      //upper middle handle
      HandleCollection[1].PointF = new PointF(Rectangle.Left + Rectangle.Width/2.0f, Rectangle.Top);
      //upper right handle
      HandleCollection[2].PointF = new PointF(Rectangle.Right, Rectangle.Top);
      //middle right handle
      HandleCollection[3].PointF = new PointF(Rectangle.Right, Rectangle.Top + Rectangle.Height/2.0f);
      //lower right handle
      HandleCollection[4].PointF = new PointF(Rectangle.Right, Rectangle.Bottom);
      //lower middle handle
      HandleCollection[5].PointF = new PointF(HandleCollection[1].PointF.X, Rectangle.Bottom);
      //lower left handle
      HandleCollection[6].PointF = new PointF(HandleCollection[0].PointF.X, Rectangle.Bottom);
      //middle left handle
      HandleCollection[7].PointF = new PointF(HandleCollection[0].PointF.X, HandleCollection[3].PointF.Y);
    }

    internal Cursor HandleHit(MouseEventArgs mouseEventArgs)
    {
      if (HandleCollection.Moving == null)
        HandleCollection.Moving = HandleCollection.Hit(mouseEventArgs.Location);
      if (HandleCollection.Moving == null)
        return Cursors.Default;

      int handle = HandleCollection.IndexOf(HandleCollection.Moving);

      if (mouseEventArgs.Button != MouseButtons.Left)
        HandleCollection.Moving = null;

      // 0 1 2
      // 7   3
      // 6 5 4
      switch (handle)
      {
        case 0:
          if (Rectangle.Height + Rectangle.Bottom - mouseEventArgs.Location.Y > 10 &&
              Rectangle.Right - mouseEventArgs.Location.X > 10)
            return Handle0Move(mouseEventArgs);
          break;
        case 1:
          if (Rectangle.Height + Rectangle.Bottom - mouseEventArgs.Location.Y > 10)
            return Handle1Move(mouseEventArgs);
          break;
        case 2:
          if (Rectangle.Height + Rectangle.Bottom - mouseEventArgs.Location.Y > 10 &&
              Rectangle.Width + mouseEventArgs.Location.X - Rectangle.Right > 10)
            return Handle2Move(mouseEventArgs);
          break;
        case 3:
          if (Rectangle.Width + mouseEventArgs.Location.X - Rectangle.Right > 10)
            return Handle3Move(mouseEventArgs);
          break;
        case 4:
          if (Rectangle.Width + mouseEventArgs.Location.X - Rectangle.Right > 10 &&
              Rectangle.Height + mouseEventArgs.Location.Y - Rectangle.Bottom > 10)
            return Handle4Move(mouseEventArgs);
          break;
        case 5:
          if (Rectangle.Height + mouseEventArgs.Location.Y - Rectangle.Bottom > 10)
            return Handle5Move(mouseEventArgs);
          break;
        case 6:
          if (Rectangle.Height + mouseEventArgs.Location.Y - Rectangle.Bottom > 10 &&
              Rectangle.Right - mouseEventArgs.Location.X > 10)
            return Handle6Move(mouseEventArgs);
          break;
        case 7:
          if (Rectangle.Right - mouseEventArgs.Location.X > 10)
            return Handle7Move(mouseEventArgs);
          break;
      }
      return Cursors.Default;
    }

    public override void Snap(PointF snap)
    {
      base.Snap(snap);
      if (HandleCollection.Moving == null) return;
      // Try to snap to border...
      // 0 1 2
      // 7   3
      // 6 5 4
      switch (HandleCollection.IndexOf(HandleCollection.Moving))
      {
        case 0:
          Snap1();
          Snap7();
          break;
        case 1:
          Snap1();
          break;
        case 2:
          Snap1();
          Snap3();
          break;
        case 3:
          Snap3();
          break;
        case 4:
          Snap3();
          Snap5();
          break;
        case 5:
          Snap5();
          break;
        case 6:
          Snap5();
          Snap7();
          break;
        case 7:
          Snap7();
          break;
      }

      HandleCollection.Moving = null;
    }

    // 0 1 2
    // 7   3
    // 6 5 4
    private void Snap7()
    {
      float deltaX = Rectangle.Left -
                     Settings.Default.SquareSize.Width *
                     (float)Math.Truncate(Rectangle.Left / Settings.Default.SquareSize.Width);
      if (Math.Abs(deltaX) > HandleSnapLimit) return;
      if (deltaX > Settings.Default.SquareSize.Width / 2) deltaX -= Settings.Default.SquareSize.Width;
      Rectangle = new RectangleF(Rectangle.X - deltaX, Rectangle.Y, Rectangle.Width + deltaX, Rectangle.Height);
    }

    // 0 1 2
    // 7   3
    // 6 5 4
    private void Snap5()
    {
      float deltaY = Rectangle.Bottom -
                     Settings.Default.SquareSize.Height *
                     (float)Math.Truncate(Rectangle.Bottom / Settings.Default.SquareSize.Height);
      if (deltaY > Settings.Default.SquareSize.Width / 2) deltaY -= Settings.Default.SquareSize.Height;
      if (Math.Abs(deltaY) > HandleSnapLimit) return;
      Rectangle = new RectangleF(Rectangle.X, Rectangle.Y, Rectangle.Width, Rectangle.Height - deltaY);
    }

    // 0 1 2
    // 7   3
    // 6 5 4
    private void Snap3()
    {
      float deltaX = Rectangle.Right -
                     Settings.Default.SquareSize.Width * 
                     (float)Math.Truncate(Rectangle.Right / Settings.Default.SquareSize.Width);
      if (deltaX > Settings.Default.SquareSize.Width / 2) deltaX -= Settings.Default.SquareSize.Width;
      if (Math.Abs(deltaX) > HandleSnapLimit) return;
      Rectangle = new RectangleF(Rectangle.X, Rectangle.Y, Rectangle.Width - deltaX, Rectangle.Height);
    }

    // 0 1 2
    // 7   3
    // 6 5 4
    private void Snap1()
    {
      float deltaY = Rectangle.Top -
                     Settings.Default.SquareSize.Height*
                     (float) Math.Truncate(Rectangle.Top/Settings.Default.SquareSize.Height);
      if (deltaY > Settings.Default.SquareSize.Width/2) deltaY -= Settings.Default.SquareSize.Height;
      if (Math.Abs(deltaY) > HandleSnapLimit) return;
      Rectangle = new RectangleF(Rectangle.X, Rectangle.Y - deltaY, Rectangle.Width, Rectangle.Height + deltaY);
    }

    private const float HandleSnapLimit = 8.0f;

    protected virtual Cursor Handle7Move(MouseEventArgs mouseEventArgs)
    {
      if (mouseEventArgs.Button == MouseButtons.Left)
        Rectangle = new RectangleF(mouseEventArgs.Location.X, Rectangle.Y,
                                   Rectangle.Right - mouseEventArgs.Location.X, Rectangle.Height);
      return Cursors.SizeWE;
    }

    protected virtual Cursor Handle6Move(MouseEventArgs mouseEventArgs)
    {
      if (mouseEventArgs.Button == MouseButtons.Left)
        Rectangle = new RectangleF(mouseEventArgs.Location.X, Rectangle.Y,
                                   Rectangle.Right - mouseEventArgs.Location.X,
                                   mouseEventArgs.Location.Y - Rectangle.Top);
      return Cursors.SizeNESW;
    }

    protected virtual Cursor Handle5Move(MouseEventArgs mouseEventArgs)
    {
      if (mouseEventArgs.Button == MouseButtons.Left)
        Rectangle = new RectangleF(Rectangle.Location, new SizeF(
                                                         Rectangle.Width,
                                                         mouseEventArgs.Location.Y - Rectangle.Top));
      return Cursors.SizeNS;
    }

    protected virtual Cursor Handle4Move(MouseEventArgs mouseEventArgs)
    {
      if (mouseEventArgs.Button == MouseButtons.Left)
        Rectangle = new RectangleF(Rectangle.Location, new SizeF(
                                                         mouseEventArgs.Location.X - Rectangle.Left,
                                                         mouseEventArgs.Location.Y - Rectangle.Top));
      return Cursors.SizeNWSE;
    }

    protected virtual Cursor Handle3Move(MouseEventArgs mouseEventArgs)
    {
      if (mouseEventArgs.Button == MouseButtons.Left)
        Rectangle = new RectangleF(Rectangle.Location,
                                   new SizeF(mouseEventArgs.Location.X - Rectangle.Left, Rectangle.Height));
      return Cursors.SizeWE;
    }

    protected virtual Cursor Handle2Move(MouseEventArgs mouseEventArgs)
    {
      if (mouseEventArgs.Button == MouseButtons.Left)
        Rectangle = new RectangleF(Rectangle.Left, mouseEventArgs.Location.Y,
                                   mouseEventArgs.Location.X - Rectangle.Left,
                                   Rectangle.Bottom - mouseEventArgs.Location.Y);
      return Cursors.SizeNESW;
    }

    protected virtual Cursor Handle1Move(MouseEventArgs mouseEventArgs)
    {
      if (mouseEventArgs.Button == MouseButtons.Left)
        Rectangle = new RectangleF(Rectangle.Left,
                                   mouseEventArgs.Location.Y,
                                   Rectangle.Width, Rectangle.Bottom - mouseEventArgs.Location.Y);
      return Cursors.SizeNS;
    }

    protected virtual Cursor Handle0Move(MouseEventArgs mouseEventArgs)
    {
      if (mouseEventArgs.Button == MouseButtons.Left)
        Rectangle = new RectangleF(mouseEventArgs.Location.X,
                                   mouseEventArgs.Location.Y,
                                   Rectangle.Right - mouseEventArgs.Location.X,
                                   Rectangle.Bottom - mouseEventArgs.Location.Y);
      return Cursors.SizeNWSE;
    }
  }
}